#ifndef GS_IREFERENCED_H
#define GS_IREFERENCED_H

#include "Config.h"

namespace Gs
{
	// Represents a reference counted API object.
	class GS_EXPIMP IReferenced
	{
	private:
		// Stores the reference count of the instance.
		int _referenceCount;

	protected:
		// Initialises a new instance of the IReferenced class.
		IReferenced();

	public:
		// Destroys the IReferenced instance.
		virtual ~IReferenced();

		// Retains a reference to the instance.
		void Retain();

		// Releases a reference to the instance.
		void Release();

		// Returns the number of references to the instance.
		int ReferenceCount();
	};

	// Represents a template reference counting container for IReferenced instances.
	// This container does not use the initial reference and must be released after containing the instance.
	template<class T>
	class Reference
	{
	public:
		// Defines the pointer type.
		typedef T *Ptr;

		// Defines the reference type.
		typedef T &Ref;

		// Initialises a new instance of the Reference<T> class.
		Reference()
			: _instance(0)
		{
		}

		// Initialises a new instance of the Reference<T> class.
		Reference(Ptr instance)
			: _instance(instance)
		{
			if (_instance)
				_instance->Retain();
		}

		// Initialises a new instance of the Reference<T> class.
		Reference(const Reference &reference)
			: _instance(reference._instance)
		{
			if (_instance)
				_instance->Retain();
		}

		// Destroys the Reference instance.
		~Reference()
		{
			Release();
		}

		// Copies an existing reference.
		Reference &operator =(const Reference &reference)
		{
			Release();
			_instance = reference._instance;
			if (_instance)
				_instance->Retain();
			return *this;
		}

		// Assigns an instance.
		Reference &operator =(Ptr instance)
		{
			Release();
			_instance = instance;
			if (_instance)
				_instance->Retain();
			return *this;
		}

		// Releases the contained reference.
		void Release()
		{
			if (_instance)
			{
				_instance->Release();
				_instance = 0;
			}
		}

		// Returns the pointer to the instance.
		Ptr operator ->()
		{
			return _instance;
		}

		// Returns a reference to the instance.
		Ref operator *()
		{
			return *_instance;
		}

		// Creates a reference from a new instance (does not retain).
		static Reference FromNew(Ptr instance)
		{
			Reference ref(instance);
			if (instance)
				instance->Release();
			return ref;
		}
			
	private:
		// Stores the instance pointer.
		Ptr _instance;
	};
}

#endif